{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9bcf09ff",
   "metadata": {},
   "source": [
    "MNIST 数据库官方网址为：http://yann.lecun.com/exdb/mnist/\n",
    "    \n",
    "也可以直接下载train-images-idx3-ubyte.gz、 train-labels-idxl-ubyte.gz等文件\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80f6a9a9",
   "metadata": {},
   "source": [
    "###  数据解析\n",
    "TensorFlow 2.0中的MNIST数据集为压缩的图像数据，解析MNIST数据集使用Keras 高层封装的接口，TensorFlow 2.0的MNIST数据集包括训练集和数据集两部分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9bbeb930",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf  # 引入Tensorflow框架\n",
    "from tensorflow import keras  # 引入keras框架\n",
    "from tensorflow.keras import layers # 引入keras层结构\n",
    "import numpy as np  # 引入数据处理模块\n",
    "import matplotlib.pyplot as plt # 引入图像处理模块\n",
    "def images_show(train_images, train_labels):\n",
    "    \"\"\"参数\n",
    "    train_images: 训练数据\n",
    "    train_labels: 训练数据标签\n",
    "    返回: 无\n",
    "    \"\"\"\n",
    "    # 打开绘图区\n",
    "    plt.figure(figsize=(6,6))\n",
    "    # 绘制前16个图像\n",
    "    for i in range(16):\n",
    "        # 图像矩阵数据\n",
    "        train_data_value = train_images[i]\n",
    "        # 图像标签数据\n",
    "        train_label_value = train_labels[i]\n",
    "        # 调整图像尺寸\n",
    "        train_image_reshape = train_data_value.reshape((28, 28, -1))\n",
    "        # 绘图区分区\n",
    "        plt.subplot(4,4,i+1)\n",
    "        plt.subplots_adjust(wspace=0.5, hspace=0.8)\n",
    "        # plt.imshow(train_image_reshape[:,:,0], cmap=\"Greys_r\")\n",
    "        # 图像写入绘图区\n",
    "        plt.imshow(train_data_value, cmap=plt.cm.binary)\n",
    "        # 添加图像标题\n",
    "        plt.title(f\"number is:{train_label_value}\")\n",
    "        # print(\"data i: {}\".format(i))\n",
    "    plt.savefig(\"./image/train_image_show.png\", format=\"png\", dpi=500)\n",
    "    plt.show()\n",
    "if __name__==\"__main__\":\n",
    "    mnist = keras.datasets.mnist      # 引入MNIST数据集  \n",
    "    # 提取数据集数据\n",
    "    (train_images, train_labels), (test_images, test_labels) = mnist.load_data()\n",
    "    print(train_images[0].reshape((28, 28, 1)).shape)\n",
    "    print(\"训练图像数据集尺寸:{}\".format(train_images.shape))\n",
    "    print(\"训练图像数量:{}\".format(len(train_images)))\n",
    "    print(\"第一个训练标签数据:{}\".format(train_labels[0]))\n",
    "    print(\"测试数据集尺寸:{}\".format(test_images.shape))\n",
    "    print(\"测试图像数量:{}\".format(len(train_images)))\n",
    "    print(\"第一个测试标签数据:{}\".format(test_labels[0]))\n",
    "    images_show(train_images,train_labels)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "940d91ae",
   "metadata": {},
   "source": [
    "# 编程实战"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14fc7cdc",
   "metadata": {},
   "source": [
    "### 1、数据的获取和处理\n",
    "\n",
    "可以使用TensorFlow 2.0自带的数据获取方式获得 MNIST 数据集并进行处理，代码如下：\n",
    "\n",
    "(train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()\n",
    "\n",
    "对于TensorFlow 2.0来说，它提供常用API并收集整理一些数据集，为模型的编写和验证带来了最大限度的方便。建议读者选择自带的API，因为大多数自带的 API，在底层都会做一定程度的优化，调用不同的库包去最大效率地实现功能\n",
    "\n",
    "通过TensorFlow提供的读取工具，获取图像和标签后，还需要将数据集的原始数据28×28×1的RGB图像数据，即图像宽为28px，高度为28px，通道数为1，使用占位符转换成一个四维数据[None, 28,28,1]，该维度数据为TensorFlow使用的标淮数据。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "003781c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "#设置GPU\n",
    "#如果使用的是CPU可以注释掉这部分的代码。\n",
    "import tensorflow as tf  # 引入Tensorflow框架\n",
    "gpus = tf.config.list_physical_devices(\"GPU\")\n",
    "\n",
    "if gpus:\n",
    "    tf.config.experimental.set_memory_growth(gpus[0], True)  #设置GPU显存用量按需使用\n",
    "    tf.config.set_visible_devices([gpus[0]],\"GPU\")\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98424596",
   "metadata": {},
   "outputs": [],
   "source": [
    "#导入相关模块\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras  # 引入keras框架\n",
    "from tensorflow.keras import layers # 引入keras层结构\n",
    "import numpy as np  # 引入数据处理模块\n",
    "import matplotlib.pyplot as plt # 引入图像处理模块\n",
    "from datetime import datetime\n",
    "def get_datas():\n",
    "    # 读取MNIST数据集\n",
    "    (train_images, train_labels), (test_images, test_labels) = keras.datasets.mnist.load_data()\n",
    "    # 获取前1000个图像数据\n",
    "    train_labels = train_labels[:1000]\n",
    "    # 获取前1000个评估使用图像\n",
    "    eval_images = train_images[:1000]\n",
    "    # 调整图像数据维度，供训练使用\n",
    "    train_images = train_images[:1000].reshape(-1, 28,28,1)/255.0\n",
    "    return train_images, train_labels, eval_images"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aaf9b3f2",
   "metadata": {},
   "source": [
    "### 2、搭建卷积神经网络\n",
    "以AlexNet模型创建为例，采用Sequential外置搭建神经网络的方法进行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed85e333",
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_model():\n",
    "    #使用keras新建神经网络\n",
    "    model = tf.keras.Sequential(name=\"MNIST-CNN\")\n",
    "    # 卷积层-1\n",
    "\n",
    "    # 最大池化层-1\n",
    "\n",
    "    # 卷积层-2\n",
    "    \n",
    "    # 最大池化层-2\n",
    "    \n",
    "     #卷积层-3\n",
    "    \n",
    "    #卷积层-4\n",
    "                          \n",
    "    #卷积层-5\n",
    "   \n",
    "    #最大池化层-3\n",
    "   \n",
    "    #全连接层-1   \n",
    "\n",
    "    #全连接层-2\n",
    "    \n",
    "    #dropout操作\n",
    "    \n",
    "    #全连接层-3\n",
    "   \n",
    "    #dropout操作\n",
    "    \n",
    "    #全连接层-4\n",
    "\n",
    "    # 配置损失计算及优化器\n",
    "    compile_model(model)\n",
    "    return model\n",
    "\n",
    "y=create_model()\n",
    "y.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78d465ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "def compile_model(model):\n",
    "    model.compile(\n",
    "        #损失函数优化器——Adam优化器\n",
    "        optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n",
    "        #损失函数——交叉熵函数\n",
    "        loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "        metrics=[\"accuracy\"] #测量值\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9341b74c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def display_nn_structure(model, nn_structure_path):\n",
    "    #展示神经网络结构\n",
    "    model.summary()\n",
    "    keras.utils.plot_model(model, nn_structure_path, show_shapes=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c43b2ef5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_model(model, inputs, outputs, model_path):\n",
    "    #训练神经网络\n",
    "    history = model.fit(\n",
    "            inputs, \n",
    "            outputs,  \n",
    "            batch_size=64, \n",
    "            epochs=5, \n",
    "            verbose=1 \n",
    "            )\n",
    "    model.save_weights(model_path+'cnn.ckpt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c44b7905",
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_model(model, model_path):    # 载入模型\n",
    "    model.load_weights(model_path+'cnn.ckpt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ea4590d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def prediction(model, model_path, inputs):\n",
    "    # 载入模型\n",
    "    load_model(model, model_path)\n",
    "    # 预测值\n",
    "    pres = model.predict(inputs)\n",
    "    print(\"prediction:{}\".format(pres))\n",
    "    # 返回预测值\n",
    "    return pres\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96a94def",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_prediction(model, model_path, inputs, evals):\n",
    "    #可视化预测结果     \n",
    "    pres = prediction(model, model_path, inputs)  # 预测值\n",
    "    pres = tf.math.argmax(pres, 1)\n",
    "    for i in range(4):\n",
    "        plt.subplot(4,4,i+1)\n",
    "        plt.subplots_adjust(wspace=0.5, hspace=0.8)\n",
    "        plt.imshow(evals[i], cmap=plt.cm.binary)\n",
    "        plt.title(\"predict:{}\".format(pres[i]))\n",
    "    plt.savefig(\"./image/cnn-pre.png\", format=\"png\", dpi=300)\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "853180a1",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "if __name__==\"__main__\":\n",
    "    model_path=\"./mnist_cnn/models\"\n",
    "    print(model_path)\n",
    "    mnist = keras.datasets.mnist      # 引入MNIST数据集  \n",
    "    # 提取数据集数据\n",
    "    inputs, outputs, evals =get_datas()\n",
    "    model=create_model()\n",
    "    display_nn_structure(model,\"./image/mnist_alexnet.png\")\n",
    "    train_model(model,inputs,outputs,model_path)\n",
    "    plot_prediction(model,model_path,inputs[:4],evals[:4]) #预测前4张图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffb9cad8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2a56676",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
